<template>
    <div class="calendar">
        <div class="content-all">
            <div class="top-change" :style="{'background': topBackgroundColor,'color':topTextColor}">
                <li @click="PreMonth(myDate,false)">
                    <div class="jiantou1" :style="{'borderColor':topTextColor}"></div>
                </li>
                <li class="content-li">{{dateTop}}</li>
                <li @click="NextMonth(myDate,false)">
                    <div class="jiantou2" :style="{'borderColor':topTextColor}"></div>
                </li>
            </div>
            <div class="content" :style="{'background': weekBackgroundColor,'color':weekTextColor}">
                <div class="content-item" v-for="tag in textTop">
                    <div class="top-tag">{{tag}}</div>
                </div>
            </div>
            <div class="content" :style="{'background': bodyBackgroundColor,'height':fixedHeight?'calc(10.66667vw * 6)':'auto'}">
                <div class="content-item" v-for="(item,index) in list" @click="clickDay(item,index)">
                    <div class="item-date"
                         :style="{'color':item.otherMonth!=='nowMonth'?disableTextColor:bodyTextColor}"
                         :class="[{ 'isMark': item.isMark},{'other-dayhide':item.otherMonth!=='nowMonth'},{'want-dayhide':item.dayHide},{'isToday':item.isToday},{'chose-day':item.chooseDay},setClass(item)]"
                    >{{item.id}}
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>

<script lang="ts">
    import {Component, Prop, Vue, Watch, Emit} from "vue-property-decorator";

    @Component({})
    //日历组件
    export default class Calendar extends Vue {
        // 如果需要某月的几天被标注，传当月的日期数组。如["2019/4/14","2019/4/15"]被会标注（相同的标记）
        @Prop({default: function() {return [];}, type: Array})
        private markDate!: Array<string>;
        // 如果需要某月的几天被标注，传当月的日期数组。如=[{date:'2019/4/14',className:"mark1"}, {date:'2019/4/15',className:"mark2"}]被会标注（多种标记）
        @Prop({default: function() {return [];}, type: Array})
        private markDateMore!: Array<object>;
        // 日历头部的文字，默认是 [ '日','一', '二', '三', '四', '五', '六'] ，可以根据自己的需求进行不同的修改。
        @Prop({default: function() {return ["一", "二", "三", "四", "五", "六", "日"];}, type: Array})
        private textTop!: Array<string>;
        // 默认是周一开始 当是true的时候 是周日开始
        @Prop({default: false, type: Boolean})
        private sundayStart!: boolean;
        // 某个日期以前的不允许点击 时间戳长度是 10 位
        @Prop({default: 0, type: Number})
        private agoDayHide!: number;
        // 某个日期以后的不允许点击 时间戳长度是 10 位
        @Prop({default: 2554387200, type: Number})
        private futureDayHide!: number;
        // 日历组件顶部背景色
        @Prop({default: "#328dea", type: String})
        private topBackgroundColor!: string;
        // 日历组件顶部文字色
        @Prop({default: "#fff", type: String})
        private topTextColor!: string;
        // 日历组件星期部分背景色
        @Prop({default: "#d7edff", type: String})
        private weekBackgroundColor!: string;
        // 日历组件星期部分文字色
        @Prop({default: "#313131", type: String})
        private weekTextColor!: string;
        // 日历本体背景色
        @Prop({default: "#f2f5f9", type: String})
        private bodyBackgroundColor!: string;
        // 日历本体文字色
        @Prop({default: "#333", type: String})
        private bodyTextColor!: string;
        // 日历本体文字色
        @Prop({default: "#bfbfbf", type: String})
        private disableTextColor!: string;
        // 日历本体文字色
        @Prop({default: true, type: Boolean})
        private fixedHeight!: boolean;


        private myDate: object = [];
        private list: Array<object> = [];
        private historyChose: Array<object> = [];
        private dateTop: string = "";

        private created() {
            this.myDate = new Date();
            this.getList(this.myDate);
        }

        private setClass(data: any) {
            let obj: any = {};
            obj[data.markClassName] = data.markClassName;
            return obj;
        }

        private clickDay(item: any, index: number): void {
            if (item.otherMonth === "nowMonth" && !item.dayHide) {
                this.getList(this.myDate, item.date);
            }
            if (item.otherMonth !== "nowMonth") {
                item.otherMonth === "preMonth" ? this.PreMonth(item.date) : this.NextMonth(item.date);
            }
        }

        private ChoseMonth(date: any, isChosedDay = true): void {
            date = this.dateFormat(date);
            this.myDate = new Date(date);
            this.changeMonth(this.dateFormat(this.myDate));
            if (isChosedDay) {
                this.getList(this.myDate, date, isChosedDay);
            } else {
                this.getList(this.myDate);
            }
        }

        private PreMonth(date: any, isChosedDay = true): void {
            date = this.dateFormat(date);
            this.myDate = this.getOtherMonth(this.myDate, "preMonth");
            this.changeMonth(this.dateFormat(this.myDate));
            if (isChosedDay) {
                this.getList(this.myDate, date, isChosedDay);
            } else {
                this.getList(this.myDate);
            }
        }

        private NextMonth(date: any, isChosedDay = true): void {
            date = this.dateFormat(date);
            this.myDate = this.getOtherMonth(this.myDate, "nextMonth");
            this.changeMonth(this.dateFormat(this.myDate));
            if (isChosedDay) {
                this.getList(this.myDate, date, isChosedDay);
            } else {
                this.getList(this.myDate);
            }
        }

        private forMatArgs(): any {
            let markDate: Array<string> = this.markDate;
            let markDateMore: Array<any> = this.markDateMore;
            markDate = markDate.map(k => {
                return this.dateFormat(k);
            });
            markDateMore = markDateMore.map(k => {
                k.date = this.dateFormat(k.date);
                return k;
            });
            return [markDate, markDateMore];
        }


        private getList(date: any, chooseDay: string = "", isChosedDay = true) {
            const [markDate, markDateMore] = this.forMatArgs();
            this.dateTop = `${date.getFullYear()}年    ${date.getMonth() + 1}月`;
            let arr: any[] = this.getMonthList(this.myDate);
            for (let i = 0; i < arr.length; i++) {
                let markClassName: string = "";
                let k: any = arr[i];
                k.chooseDay = false;
                const nowTime: any = k.date;
                const t: number = new Date(nowTime).getTime() / 1000;
                //看每一天的class
                for (const c of markDateMore) {
                    if (c.date === nowTime) {
                        markClassName = c.className || "";
                    }
                }
                //标记选中某些天 设置class
                k.markClassName = markClassName;
                k.isMark = markDate.indexOf(nowTime) > -1;
                //无法选中某天
                k.dayHide = t < this.agoDayHide || t > this.futureDayHide;
                if (k.isToday) {
                    this.isToday(nowTime);
                }
                let flag = !k.dayHide && k.otherMonth === "nowMonth";
                if (chooseDay && chooseDay === nowTime && flag) {
                    this.changeMonth(nowTime);
                    this.historyChose.push(nowTime);
                    k.chooseDay = true;
                } else if (
                    this.historyChose[this.historyChose.length - 1] === nowTime &&
                    !chooseDay &&
                    flag
                ) {
                    k.chooseDay = true;
                }
            }
            this.list = arr;
        }

        //切换月份调用的方法，返回切换到某月的日期
        //@arg date:YY-MM-DD
        @Emit("changeMonth")
        private changeMonth(date: string) {
        }

        //选中某天调用的方法，返回选中的日期
        //@arg date:YY-MM-DD
        @Emit("choseDay")
        private choseDay(date: string) {

        }

        // 切换月份的时候，如果切到当前月份，调用这个方法，返回当前月今天
        //@arg date:YY-MM-DD
        @Emit("isToday")
        private isToday(data: string) {

        }


        // 以下全是内部私有方法
        // 当某月的天数
        private getDaysInOneMonth(date: any): number {
            const year: number = date.getFullYear();
            const month: number = date.getMonth() + 1;
            const d: any = new Date(year, month, 0);
            return d.getDate();
        }

        // 向前空几个
        private getMonthweek(date: any): number {
            const year: number = date.getFullYear();
            const month: number = date.getMonth() + 1;
            const dateFirstOne: any = new Date(year + "/" + month + "/1");
            return this.sundayStart ?
                dateFirstOne.getDay() == 0 ? 7 : dateFirstOne.getDay() :
                dateFirstOne.getDay() == 0 ? 6 : dateFirstOne.getDay() - 1;
        }

        // format日期
        private dateFormat(date: string | any): string {
            date = (typeof date === "string" ? new Date(date.replace(/\-/g, "/")) : date);
            return date.getFullYear() + "/" + (date.getMonth() + 1) + "/" + date.getDate();
        }

        // 获取当前日期上个月或者下个月
        private getOtherMonth(date: any, str: string = "nextMonth"): any {
            const timeArray: string[] = this.dateFormat(date).split("/");
            const year: number = Number(timeArray[0]);
            const month: number = Number(timeArray[1]);
            const day: number = Number(timeArray[2]);
            let year2: number = year;
            let month2: number | string;
            if (str === "nextMonth") {
                month2 = month + 1;
                if (month2 == 13) {
                    year2 = year2 + 1;
                    month2 = 1;
                }
            } else {
                month2 = month - 1;
                if (month2 == 0) {
                    year2 = year2 - 1;
                    month2 = 12;
                }
            }
            let day2: number | string = day;
            const days2: number = new Date(year2, month2, 0).getDate();
            if (day2 > days2) {
                day2 = days2;
            }
            if (month2 < 10) {
                month2 = "0" + month2;
            }
            if (day2 < 10) {
                day2 = "0" + day2;
            }
            return new Date(year2 + "/" + month2 + "/" + day2);
        }

        // 上个月末尾的一些日期
        private getLeftArr(date: any) {
            const arr: Object[] = [];
            const leftNum: number = this.getMonthweek(date);
            const num: number = this.getDaysInOneMonth(this.getOtherMonth(date, "preMonth")) - leftNum + 1;
            const preDate: any = this.getOtherMonth(date, "preMonth");
            // 上个月多少开始
            for (let i = 0; i < leftNum; i++) {
                const nowTime: string = preDate.getFullYear() + "/" + (preDate.getMonth() + 1) + "/" + (num + i);
                arr.push({
                    id: num + i,
                    date: nowTime,
                    isToday: false,
                    otherMonth: "preMonth",
                });
            }
            return arr;
        }

        // 下个月末尾的一些日期
        private getRightArr(date: object) {
            const arr: Object[] = [];
            const nextDate: any = this.getOtherMonth(date, "nextMonth");
            const leftLength: number = this.getDaysInOneMonth(date) + this.getMonthweek(date);
            const length: number = 7 - leftLength % 7;
            for (let i = 0; i < length; i++) {
                const nowTime: string = nextDate.getFullYear() + "/" + (nextDate.getMonth() + 1) + "/" + (i + 1);
                arr.push({
                    id: i + 1,
                    date: nowTime,
                    isToday: false,
                    otherMonth: "nextMonth",
                });
            }
            return arr;
        }

        // 获取某月的列表不包括上月和下月
        private getMonthListNoOther(date: any) {
            const arr: Object[] = [];
            const num: number = this.getDaysInOneMonth(date);
            const year: number = date.getFullYear();
            const month: number = date.getMonth() + 1;
            const toDay: string = this.dateFormat(new Date());
            for (let i = 0; i < num; i++) {
                const nowTime: string = year + "/" + month + "/" + (i + 1);
                arr.push({
                    id: i + 1,
                    date: nowTime,
                    isToday: toDay === nowTime,
                    otherMonth: "nowMonth",
                });
            }
            return arr;
        }

        // 获取某月的列表 用于渲染
        private getMonthList(date: object): Array<object> {
            return [...this.getLeftArr(date), ...this.getMonthListNoOther(date), ...this.getRightArr(date)];
        }


    }
</script>

<style scoped lang="stylus">
    .calendar
        width 100%;
        display block;

        .top-change
            display: flex;


        .top-change li
            cursor: pointer;
            display: flex;
            color: inherit;
            font-size: 18px;
            flex: 1;
            justify-content: center;
            align-items: center;
            height: 47px;


        .top-change .content-li
            cursor: auto;
            flex: 2.5;

        .content-all
            width: 100%;
            overflow: hidden;
            padding-bottom: 8px;


        .content
            display: flex;
            flex-wrap: wrap;
            padding: 0 3% 0 3%;
            width: 100%;


        .content:first-child .content-item-tag,
        .content:first-child .content-item
            color: #ddd;
            font-size: 16px;


        .content-item, .content-item-tag
            font-size: 15px;
            width: 14.28%;
            text-align: center;
            color: inherit;
            position: relative;

        .content-item
            height: 40px;


        .top-tag
            width: 40px;
            height: 40px;
            line-height: 40px;
            margin: auto;
            display: flex;
            justify-content: center;
            align-items: center;


        .item-date
            width: 40px;
            height: 40px;
            line-height: 40px;
            margin: auto;
            display: flex;
            justify-content: center;
            align-items: center;


        .jiantou1
            width: 12px;
            height: 12px;
            border-top: 2px solid #ffffff;
            border-left: 2px solid #ffffff;
            transform: rotate(-45deg);


        .jiantou1:active, .jiantou2:active
            border-color: #ddd;


        .jiantou2
            width: 12px;
            height: 12px;
            border-top: 2px solid #ffffff;
            border-right: 2px solid #ffffff;
            transform: rotate(45deg);

        .content-item > .isMark
            margin: auto;
            border-radius: 100px;
            background: blue;
            z-index: 2;

        .content-item .other-dayhide
            color: #bfbfbf;

        .content-item .want-dayhide
            color: #bfbfbf;

        .content-item .isToday
            color #09F !important;

        .content-item .chose-day
            background: #ddd !important;


</style>
